home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / proc / procTimer.c < prev    next >
C/C++ Source or Header  |  1990-09-12  |  15KB  |  527 lines

  1. /* 
  2.  * procTimer.c --
  3.  *
  4.  *    Routines to manipulate the interval timers of a process.
  5.  *
  6.  * Copyright 1987, 1988 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /sprite/src/kernel/proc/RCS/procTimer.c,v 9.3 90/09/12 13:58:24 jhh Exp $ SPRITE (Berkeley)";
  18. #endif /* not lint */
  19.  
  20.  
  21. #include <sprite.h>
  22. #include <proc.h>
  23. #include <procInt.h>
  24. #include <timer.h>
  25. #include <sys.h>
  26. #include <sig.h>
  27. #include <stdlib.h>
  28. #include <sync.h>
  29. #include <bstring.h>
  30.  
  31. /*
  32.  * Information about the state of an interval timer for a process.
  33.  * There are several types of interval timers and the info for all of
  34.  * them are kept in an array pointed to by the PCB entry.
  35.  */
  36. typedef struct ProcIntTimerInfo {
  37.     Timer_Ticks    expire;        /* When the timer is to expire. */
  38.     Timer_Ticks    interval;    /* Amount of time between expirations.
  39.                  * If zero, then the timer is not restarted 
  40.                  * after the first expiration. */
  41.     ClientData    token;        /* Token returned by Proc_CallFuncAbsTime. 
  42.                  * Used by the signal-sending func to check if
  43.                  * a timer has been cancelled. */
  44. } ProcIntTimerInfo;
  45.  
  46.  
  47. /*
  48.  * Monitor lock to serialize access to timer callback queue elements.
  49.  * This could be changed to a per-process monitor lock if contention
  50.  * is a problem.
  51.  */
  52. static Sync_Lock    procTimerLock = Sync_LockInitStatic("procTimerLock");
  53. #define    LOCKPTR &procTimerLock
  54.  
  55. static ReturnStatus    GetCurrentTimer _ARGS_((Proc_ControlBlock *procPtr,
  56.                 int timerType, Proc_TimerInterval *timerBufPtr,
  57.                 Boolean userMode));
  58. static void        SendTimerSigFunc _ARGS_((ClientData data,
  59.                 Proc_CallInfo *infoPtr));
  60.  
  61.  
  62.  
  63. /*
  64.  *----------------------------------------------------------------------
  65.  *
  66.  * Proc_GetIntervalTimer --
  67.  *
  68.  *    Retrieves the current value of the interval timer. If the timer
  69.  *    is not set, zero time values are returned.
  70.  *    
  71.  *
  72.  * Results:
  73.  *    SUCCESS            - the timer value was returned.
  74.  *    GEN_INVALID_ARG        - unknown timer type.
  75.  *    SYS_ARG_NOACCESS    - the timer value could not be accessed.
  76.  *
  77.  * Side effects:
  78.  *    None.
  79.  *
  80.  *----------------------------------------------------------------------
  81.  */
  82.  
  83. ENTRY ReturnStatus
  84. Proc_GetIntervalTimer(timerType, userTimerPtr)
  85.     int            timerType;    /* What type of timer: one of 
  86.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  87.                      * PROC_TIMER_PROFILE. */
  88.     Proc_TimerInterval    *userTimerPtr;    /* Buffer to store the current value 
  89.                      * of the interval timer. */
  90. {
  91.     ReturnStatus status;
  92.     Proc_ControlBlock    *procPtr;
  93.  
  94.     LOCK_MONITOR;
  95.     
  96.     if (timerType < 0 || timerType > PROC_MAX_TIMER) {
  97.     status = GEN_INVALID_ARG;
  98.     goto done;
  99.     }
  100.  
  101.     if (userTimerPtr == USER_NIL) {
  102.     status = SYS_ARG_NOACCESS;
  103.     goto done;
  104.     }
  105.  
  106.     procPtr = Proc_GetEffectiveProc();
  107.     if (procPtr == (Proc_ControlBlock *) NIL) {
  108.     panic("Proc_GetIntervalTime: current procPtr == NIL\n");
  109.     /*
  110.      * Just in case someone tries to continue.
  111.      */
  112.     status = FAILURE;
  113.     goto done;
  114.     }
  115.     Proc_Lock(procPtr);
  116.  
  117.     status = GetCurrentTimer(procPtr, timerType, userTimerPtr, TRUE);
  118.  
  119.     Proc_Unlock(procPtr);
  120. done:
  121.     UNLOCK_MONITOR;
  122.     return(status);
  123. }
  124.  
  125. /*
  126.  *----------------------------------------------------------------------
  127.  *
  128.  * GetCurrentTimer --
  129.  *
  130.  *    An internal routine to get the current value of an interval timer
  131.  *    and copy it to the user's address space.
  132.  *
  133.  *    Note: this routine assumes the process's proc table entry is locked.
  134.  *
  135.  * Results:
  136.  *    SUCCESS            - the timer value was returned.
  137.  *    SYS_ARG_NOACCESS    - the timer value could not be accessed.
  138.  *
  139.  * Side effects:
  140.  *    None.
  141.  *
  142.  *----------------------------------------------------------------------
  143.  */
  144.  
  145. static INTERNAL ReturnStatus
  146. GetCurrentTimer(procPtr, timerType, timerBufPtr, userMode)
  147.     Proc_ControlBlock    *procPtr;    /* Process to get the timer value 
  148.                      * from. */
  149.     int            timerType;    /* What type of timer: one of 
  150.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  151.                      * PROC_TIMER_PROFILE. */
  152.     Proc_TimerInterval    *timerBufPtr;    /* Buffer to store the current value 
  153.                      * of the interval timer. */
  154.     Boolean        userMode;    /* TRUE if timerBufPtr is in user
  155.                      * space (normal case).  FALSE
  156.                      * for encapsulation.
  157.                      */
  158. {
  159.     register ProcIntTimerInfo    *timerPtr = (ProcIntTimerInfo *) NIL;
  160.     Proc_TimerInterval    timer;
  161.     Boolean exists = FALSE;
  162.  
  163.     if (procPtr->timerArray != (ProcIntTimerInfo  *) NIL) {
  164.     timerPtr = &procPtr->timerArray[timerType];
  165.     if (timerPtr->token != (ClientData) NIL) {
  166.         exists = TRUE;
  167.     }
  168.     }
  169.     if (!exists) {
  170.  
  171.     /*
  172.      * No timer is scheduled. Just return zero values.
  173.      */
  174.     timer.interval.seconds = 0;
  175.     timer.interval.microseconds = 0;
  176.     timer.curValue.seconds = 0;
  177.     timer.curValue.microseconds = 0;
  178.     } else {
  179.     Timer_Ticks temp;
  180.  
  181.     Timer_TicksToTime(timerPtr->interval, &timer.interval);
  182.  
  183.     /*
  184.      * Get the amount of time remaining before the timer's expiration.
  185.      */
  186.     Timer_GetCurrentTicks(&temp);
  187.     Timer_SubtractTicks(timerPtr->expire, temp, &temp);
  188.     Timer_TicksToTime(temp, &timer.curValue);
  189.     }
  190.  
  191.     if (userMode) {
  192.     if (Proc_ByteCopy(FALSE, sizeof(timer), 
  193.               (Address) &timer, (Address) timerBufPtr) != SUCCESS) {
  194.         return(SYS_ARG_NOACCESS);
  195.     }
  196.     } else {
  197.     bcopy((Address) &timer, (Address) timerBufPtr, sizeof(timer));
  198.     }
  199.  
  200.     return(SUCCESS);
  201. }
  202.  
  203. /*
  204.  *----------------------------------------------------------------------
  205.  *
  206.  * Proc_SetIntervalTimer --
  207.  *
  208.  *    Start or cancel an interval timer for a process.  This is the
  209.  *    system call version, which calls a more general routine.
  210.  *
  211.  * Results:
  212.  *    SUCCESS            - the timer was started or stopped.
  213.  *    GEN_INVALID_ARG        - unknown timer type or invalid time value.
  214.  *    SYS_ARG_NOACCESS    - a timer value could not be accessed.
  215.  *
  216.  * Side effects:
  217.  *    A CallFunc process might be scheduled. The process's PCB entry
  218.  *    is updated.
  219.  *
  220.  *----------------------------------------------------------------------
  221.  */
  222.  
  223. ENTRY ReturnStatus
  224. Proc_SetIntervalTimer(timerType, newTimerPtr, oldTimerPtr)
  225.     int            timerType;    /* What type of timer: one of 
  226.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  227.                      * PROC_TIMER_PROFILE. */
  228.     Proc_TimerInterval    *newTimerPtr;    /* Buffer that holds a new value for
  229.                      * the interval timer. */
  230.     Proc_TimerInterval    *oldTimerPtr;    /* Buffer to hold the former value of
  231.                      * the timer. */
  232. {
  233.     return(ProcChangeTimer(timerType, newTimerPtr, oldTimerPtr, TRUE));
  234. }
  235.  
  236.  
  237. /*
  238.  *----------------------------------------------------------------------
  239.  *
  240.  * ProcChangeTimer --
  241.  *
  242.  *    Start or cancel an interval timer for a process.  This can
  243.  *    be called from kernel mode as well as user mode.
  244.  *
  245.  * Results:
  246.  *    SUCCESS            - the timer was started or stopped.
  247.  *    GEN_INVALID_ARG        - unknown timer type or invalid time value.
  248.  *    SYS_ARG_NOACCESS    - a timer value could not be accessed.
  249.  *
  250.  * Side effects:
  251.  *    A CallFunc process might be scheduled. The process's PCB entry
  252.  *    is updated.
  253.  *
  254.  *----------------------------------------------------------------------
  255.  */
  256.  
  257. ENTRY ReturnStatus
  258. ProcChangeTimer(timerType, newTimerPtr, oldTimerPtr, userMode)
  259.     int            timerType;    /* What type of timer: one of 
  260.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  261.                      * PROC_TIMER_PROFILE. */
  262.     Proc_TimerInterval    *newTimerPtr;    /* Buffer that holds a new value for
  263.                      * the interval timer. */
  264.     Proc_TimerInterval    *oldTimerPtr;    /* Buffer to hold the former value of
  265.                      * the timer. */
  266.     Boolean        userMode;    /* TRUE if intervals are in user
  267.                      * space (normal case).  FALSE
  268.                      * for encapsulation.
  269.                      */
  270. {
  271.     register ProcIntTimerInfo    *timerPtr;
  272.     register Proc_ControlBlock    *procPtr;
  273.     Proc_TimerInterval    newTimer;
  274.  
  275.     LOCK_MONITOR;
  276.  
  277.     if (timerType < 0 || timerType > PROC_MAX_TIMER) {
  278.     UNLOCK_MONITOR;
  279.     return(GEN_INVALID_ARG);
  280.     }
  281.  
  282.     procPtr = Proc_GetEffectiveProc();
  283.     if (procPtr == (Proc_ControlBlock *) NIL) {
  284.     UNLOCK_MONITOR;
  285.     panic("Proc_SetIntervalTime: procPtr == NIL\n");
  286.     return(FAILURE);
  287.     }
  288.     Proc_Lock(procPtr);
  289.  
  290.     if (procPtr->timerArray == (ProcIntTimerInfo  *) NIL) {
  291.     int j;
  292.  
  293.     /*
  294.      * The table hasn't been initialized yet. Allocate enough entries
  295.      * for all the timers. The memory won't be deallocated when the
  296.      * process dies so it can be reused by the next process using the
  297.      * PCB entry.
  298.      */
  299.     procPtr->timerArray = (ProcIntTimerInfo  *)
  300.         malloc(sizeof(ProcIntTimerInfo) * (PROC_MAX_TIMER +1));
  301.  
  302.     for (j = 0; j <= PROC_MAX_TIMER; j++) {
  303.         procPtr->timerArray[j].token = (ClientData) NIL;
  304.     }
  305.     }
  306.     timerPtr = &procPtr->timerArray[timerType];
  307.  
  308.     /*
  309.      * Return the current value if the user wants it.
  310.      */
  311.     if (oldTimerPtr != USER_NIL) {
  312.     if (GetCurrentTimer(procPtr, timerType, oldTimerPtr,
  313.                 userMode) != SUCCESS) {
  314.         Proc_Unlock(procPtr);
  315.         UNLOCK_MONITOR;
  316.         return(SYS_ARG_NOACCESS);
  317.     }
  318.     }
  319.  
  320.     /*
  321.      * Copy the new timer value from user space or a kernel buffer.
  322.      */
  323.     if (userMode) {
  324.     if (Proc_ByteCopy(TRUE, sizeof(newTimer), 
  325.         (Address) newTimerPtr, (Address) &newTimer) != SUCCESS) {
  326.         Proc_Unlock(procPtr);
  327.         UNLOCK_MONITOR;
  328.         return(SYS_ARG_NOACCESS);
  329.     }
  330.     } else {
  331.     bcopy((Address) newTimerPtr, (Address) &newTimer, sizeof(newTimer));
  332.     }
  333.  
  334.     if ((newTimer.curValue.seconds == 0) && 
  335.     (newTimer.curValue.microseconds == 0)) {
  336.  
  337.     /*
  338.      * The user wants to cancel the timer.  Invalidate the token for
  339.      * the existing expiration routine, and cancel the timer.
  340.      */
  341.     if (timerPtr->token != (ClientData) NIL) {
  342.         Proc_CancelCallFunc(timerPtr->token);
  343.         timerPtr->token = (ClientData) NIL;
  344.     }
  345.     } else {
  346.     Timer_Ticks curTime;
  347.  
  348.     /*
  349.      * Make sure the times are valid and within the clock's resolution.
  350.      */
  351.     if ((newTimer.curValue.seconds < 0) || 
  352.         (newTimer.curValue.microseconds < 0) ||
  353.         (newTimer.curValue.microseconds > ONE_SECOND) ||
  354.         (newTimer.interval.seconds < 0) || 
  355.         (newTimer.interval.microseconds < 0) ||
  356.         (newTimer.interval.microseconds > ONE_SECOND)) {
  357.  
  358.         Proc_Unlock(procPtr);
  359.         UNLOCK_MONITOR;
  360.         return(GEN_INVALID_ARG);
  361.     }
  362.     if ((newTimer.curValue.seconds == 0) && 
  363.         (newTimer.curValue.microseconds < TIMER_CALLBACK_INTERVAL_APPROX)) {
  364.         newTimer.curValue.microseconds = TIMER_CALLBACK_INTERVAL_APPROX;
  365.     }
  366.     if ((newTimer.interval.seconds == 0) && 
  367.         (newTimer.interval.microseconds > 0) &&
  368.         (newTimer.interval.microseconds < TIMER_CALLBACK_INTERVAL_APPROX)) {
  369.         newTimer.interval.microseconds = TIMER_CALLBACK_INTERVAL_APPROX;
  370.     }
  371.  
  372.     Timer_TimeToTicks(newTimer.interval, &timerPtr->interval);
  373.     Timer_TimeToTicks(newTimer.curValue, &timerPtr->expire);
  374.  
  375.     Timer_GetCurrentTicks(&curTime);
  376.     Timer_AddTicks(curTime, timerPtr->expire, &timerPtr->expire);
  377.  
  378.     /*
  379.      * Setting the token implicitly cancels a previous expiration
  380.      * routine's callback, but let's clear the old one to avoid
  381.      * putting cruft in the timer queue.
  382.      */
  383.     if (timerPtr->token != (ClientData) NIL) {
  384.         Proc_CancelCallFunc(timerPtr->token);
  385.     }
  386.     timerPtr->token = Proc_CallFuncAbsTime(SendTimerSigFunc, 
  387.             (ClientData) procPtr->processID, timerPtr->expire);
  388.     }
  389.  
  390.     Proc_Unlock(procPtr);
  391.     UNLOCK_MONITOR;
  392.     return(SUCCESS);
  393. }
  394.  
  395. /*
  396.  *----------------------------------------------------------------------
  397.  *
  398.  * SendTimerSigFunc --
  399.  *
  400.  *    Called when one of a process's interval timers has expired. This
  401.  *    routine sends a SIG_TIMER signal to process (the signal subcode is
  402.  *    the timer type, as defined in user/proc.h).
  403.  *
  404.  *
  405.  * Results:
  406.  *    None.
  407.  *
  408.  * Side effects:
  409.  *    A signal is sent to the process. A call-func process may be 
  410.  *    scheduled to send a signal in the future.
  411.  *
  412.  *----------------------------------------------------------------------
  413.  */
  414.  
  415. static ENTRY void
  416. SendTimerSigFunc(data, infoPtr)
  417.     ClientData        data;        /* Really the ID of the process that
  418.                      * should get the signal. */
  419.     Proc_CallInfo    *infoPtr;    /* Used to compare the token in the
  420.                      * PCB entry to make sure the signal
  421.                      * is wanted. */
  422. {
  423.     Proc_ControlBlock    *procPtr;
  424.     register ProcIntTimerInfo    *timerPtr;
  425.  
  426.     LOCK_MONITOR;
  427.     /*
  428.      * If the process has died, the procPtr will be NIL. 
  429.      */
  430.  
  431.     procPtr = Proc_LockPID((Proc_PID) data);
  432.     if (procPtr != (Proc_ControlBlock *) NIL) {
  433.     int i;
  434.  
  435.     /*
  436.      * Scan all the timer state info to see which timer expired.
  437.      * If our token matches the one in the timer table entry, then
  438.      * send the signal (the subcode is the timer type). If there's no 
  439.      * match after scanning all the timers, then the user cancelled the
  440.      * a timer so there's nothing to do.
  441.      */
  442.     for (i = 0; i <= PROC_MAX_TIMER; i++) {
  443.  
  444.         if (procPtr->timerArray == (ProcIntTimerInfo  *) NIL) {
  445.         /*
  446.          * This should not happen: why did we get scheduled if
  447.          * there aren't any timers?
  448.          */
  449.         panic("SendTimerSigFunc: null timer table!\n");
  450.         break;
  451.         }
  452.  
  453.         timerPtr = &procPtr->timerArray[i];
  454.         if (timerPtr->token == infoPtr->token) {
  455.         (void) Sig_SendProc(procPtr, SIG_TIMER, i, (Address)0);
  456.  
  457.         /*
  458.          * See if the signal is supposed to be repeated in the future.
  459.          */
  460.         if (Timer_TickEQ(timerPtr->interval, timer_TicksZeroSeconds)){
  461.             /*
  462.              * Nope -- all done.
  463.              */
  464.             timerPtr->token = (ClientData) NIL;
  465.         } else {
  466.             /*
  467.              * A signal is wanted in "interval" seconds from now.
  468.              * Add the interval to the expiration time instead of
  469.              * the current time to prevent drift.
  470.              */
  471.  
  472.             Timer_AddTicks(timerPtr->interval, timerPtr->expire, 
  473.                     &timerPtr->expire);
  474.             timerPtr->token = Proc_CallFuncAbsTime(SendTimerSigFunc,
  475.                     data, timerPtr->expire);
  476.         }
  477.         break;
  478.         }
  479.     }
  480.     Proc_Unlock(procPtr);
  481.     }
  482.     UNLOCK_MONITOR;
  483. }
  484.  
  485.  
  486. /*
  487.  *----------------------------------------------------------------------
  488.  *
  489.  * ProcDeleteTimers --
  490.  *
  491.  *    Cancel all interval timers for a process.  Performed on exit.
  492.  *    ProcPtr is assumed to be locked on entry.
  493.  *
  494.  * Results:
  495.  *    None.
  496.  *
  497.  * Side effects:
  498.  *    A CallFunc process might be descheduled. 
  499.  *
  500.  *----------------------------------------------------------------------
  501.  */
  502.  
  503. ENTRY void
  504. ProcDeleteTimers(procPtr)
  505.     register Proc_ControlBlock    *procPtr;
  506. {
  507.     register ProcIntTimerInfo    *timerPtr;
  508.     int j;
  509.  
  510.     LOCK_MONITOR;
  511.  
  512.     if (procPtr->timerArray == (ProcIntTimerInfo  *) NIL) {
  513.     goto done;
  514.     }
  515.  
  516.     for (j = 0; j <= PROC_MAX_TIMER; j++) {
  517.     timerPtr = &procPtr->timerArray[j];
  518.     if (timerPtr->token != (ClientData) NIL) {
  519.         Proc_CancelCallFunc(timerPtr->token);
  520.         timerPtr->token = (ClientData) NIL;
  521.     }
  522.     }
  523.     done:
  524.     UNLOCK_MONITOR;
  525. }
  526.  
  527.